#include <transfert-client.h>

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <signal.h>

#include <constants.h>
#include <log.h>
#include <lock.h>
#include <config-client.h>
#include <tri-fichier.h>

/* Gestion de la reception des rapports */

unsigned int childsForReport = 0;

void getreport_child(){
	int status;
	wait(&status);

	--childsForReport;
	if(childsForReport<0) childsForReport=0;
	
	signal(SIGCHLD, getreport_child);
}

int GetReportAllServers(){
	if(childsForReport!=0){
		return 0;
	}
	
	LogInfo("Lancement d'une procédure de récupération des rapports");
	
	while(ServerName(childsForReport)!=NULL){
		siginterrupt(SIGCHLD, 0);
		signal(SIGCHLD, getreport_child);
		int pid = fork();
		switch(pid){
			case -1:{
				LogWarning("Création échouée d'un fork pour reception d'un rapport");
			}
			case 0:{
				int res=GetReportOneServer(ServerName(childsForReport), GetIPAdress(ServerAdress(childsForReport)));
				if(res==0){
					char fileName[128];
					strcpy(fileName, CLIENT_REPORT_INCOMING);
					strcat(fileName, ServerName(childsForReport));

					int fd=open(fileName, O_WRONLY | O_CREAT | O_TRUNC);
					if(fd==-1){
						LogWarning("Impossibilité de créer un nouveau fichier de rapport");
						exit(1);
					}else{
						char *dt = GetDateTime();
						write(fd,dt,sizeof(char)*strlen(dt));
						write(fd, " :\n", 3);
						
						write(fd,"Le rapport n'a pu être obtenu.\n", sizeof(char)*strlen("Le rapport n'a pu être obtenu.\n"));
						close(fd);
					}
				}
				AddReport(ServerName(childsForReport));
				exit(0);
			}
		}
		++childsForReport;	
	}

	while(childsForReport!=0){
		usleep(20);
	}
	LogInfo("Fin de la procédure de récupération des rapports");
	return 1;
}

int receiveBreak = 0;

void getreport_watchdog(){
	receiveBreak = 1;
	return;
}

int GetReportOneServer(char* name, char* adress){
	if(adress==NULL){
		return 0;
	}
	int sd;
	sd = socket(AF_INET, SOCK_STREAM, 0);
	if(sd<0){
		char message[128] = "La création du socket a échoué sur le test du serveur : ";
		LogWarning(strcat(message, name));
		return 0;
	}

	struct sockaddr_in sai;
	sai.sin_family = AF_INET;
	sai.sin_port = htons(ConfQubePort());
	sai.sin_addr.s_addr = inet_addr(adress);

	siginterrupt(SIGALRM, 1);
	signal(SIGALRM, getreport_watchdog);
	alarm(QUBE_WATCHDOG_TIMEOUT);

	if(connect(sd, &sai, sizeof(sai))<0){
		signal(SIGALRM, SIG_IGN);
		alarm(0);
		return 0;
	}
	write(sd, "*GETR", sizeof(char)*6);

	char buffer[QUBE_BUFFER_SIZE] = "\000";
	int nb;
	
	alarm(QUBE_WATCHDOG_TIMEOUT);

	read(sd, buffer, sizeof(char)*6);
	if(strncmp(buffer, "*OKBG", 5)!=0){
		signal(SIGALRM, SIG_IGN);
		alarm(0);
		return 0;
	}

	alarm(QUBE_WATCHDOG_TIMEOUT*5);

	nb=read(sd, buffer, sizeof(char)*20);
	buffer[nb]='\00';
	if(strncmp(buffer, "*TRAN", 5)!=0){
		signal(SIGALRM, SIG_IGN);
		alarm(0);
		return 0;
	}

	long int fSize;
	if(sscanf(buffer, "*TRAN%ld", &fSize)!=1){
		return 0;
	}
	
	if(fSize<=0){
		return 0;
	}
	
	write(sd, "*OKGO", 5);

	char fileName[128];
	strcpy(fileName, CLIENT_REPORT_INCOMING);
	strcat(fileName, name);

	int fd=open(fileName, O_WRONLY | O_CREAT | O_TRUNC);
	if(fd==-1){
		signal(SIGALRM, SIG_IGN);
		alarm(0);
		return 0;
	}

	alarm(QUBE_WATCHDOG_TIMEOUT*2);
	
	char *dt = GetDateTime();
	write(fd,dt,sizeof(char)*strlen(dt));
	write(fd, " :\n", 3);

	receiveBreak=0;
	while(nb=read(sd, buffer, QUBE_BUFFER_SIZE)){
		if(receiveBreak==1) break;
		write(fd, buffer, sizeof(char)*nb);
		fSize-=nb;
		if(fSize==0) break;
	}
	
	alarm(0);
	signal(SIGALRM, SIG_IGN);
	
	close(fd);
	
	if(receiveBreak==1){
		return 0;
	}

	return 1;
}




/* Gestion des tests */

unsigned int childsForTest = 0;

void test_child(){
	int status;
	wait(&status);
	--childsForTest;
	if(childsForTest<0) childsForReport=0;
	
	signal(SIGCHLD, test_child);
}

int TestAllServers(){
	if(childsForTest>0){
		return 0;
	}
	LogInfo("Lancement d'une procédure de tests de connexions");
	UnlockAFile(CLIENT_TESTS_RESULTS);

	while(ServerName(childsForTest)!=NULL){
		int fd=open(CLIENT_TESTS_RESULTS, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
		if(fd<0){
			LogWarning("Impossible d'inscrire le résultat du test de connexion");
			return 0;
		}else{
			close(fd);
		}
		siginterrupt(SIGCHLD, 0);
		signal(SIGCHLD, test_child);
		int pid = fork();
		switch(pid){
			case -1:{
				LogWarning("Création échouée d'un fork pour test");
			}
			case 0:{
				int res=TestOneServer(ServerName(childsForTest), GetIPAdress(ServerAdress(childsForTest)));
				int wDog=0;
				while(LockAFile(CLIENT_TESTS_RESULTS)==0){
					if(++wDog==50){
						UnlockAFile(CLIENT_TESTS_RESULTS);
						wDog=0;
					}
					usleep(5);
				}

				int fd=open(CLIENT_TESTS_RESULTS, O_WRONLY | O_APPEND);
				if(fd<0){
					LogWarning("Impossible d'inscrire le résultat de test");
					return 0;
				}

				char message[128] = " ";
				strcat(message, ServerName(childsForTest));
				while(strlen(message)<16){
					strcat(message, " ");
				}

				if(res==0){
					strcat(message, " : (EE) Connexion &eacute;chou&eacute;e\n");
				}else{
					strcat(message, " : (II) Connexion &eacute;tablie\n");
				}
				write(fd, message, sizeof(char)*strlen(message));
				close(fd);
				UnlockAFile(CLIENT_TESTS_RESULTS);
				exit(0);
			}
		}
		++childsForTest;	
	}

	while(childsForTest!=0){
		usleep(200);
	}

	LogInfo("Fin de la procédure de tests");

	return 1;
}

void test_watchdog(){
	return;
}

int TestOneServer(char* name, char* adress){
	if(adress==NULL){
		return 0;
	}
	int sd;
	sd = socket(AF_INET, SOCK_STREAM, 0);
	if(sd<0){
		char message[128] = "La création du socket a échoué sur le test du serveur : ";
		LogWarning(strcat(message, name));
		return 0;
	}

	struct sockaddr_in sai;
	sai.sin_family = AF_INET;
	sai.sin_port = htons(ConfQubePort());
	sai.sin_addr.s_addr = inet_addr(adress);

	siginterrupt(SIGALRM, 1);
	signal(SIGALRM, test_watchdog);
	alarm(QUBE_WATCHDOG_TIMEOUT);

	if(connect(sd, &sai, sizeof(sai))<0){
		return 0;
	}

	write(sd, "*TTCN", sizeof(char)*6);

	char res[6] = "*NOOK";
	read(sd, &res, 5);

	close(sd);

	alarm(0);
	signal(SIGALRM, SIG_IGN);
	
	if(strncmp(res, "*CNOK", 5)==0){
		return 1;
	}
	return 0;
}

char* GetIPAdress(char* name){
	struct hostent* host;
	host = gethostbyname(name);
	
	if(host==NULL){
		return NULL;
	}else{
		return inet_ntoa(*(struct in_addr*)(host->h_addr_list[0]));
	}
}
